#include <stdarg.h>
#include "string.h"
#include "sub.h"
#include "tty.h"
#include "global.h"
#include "console.h"
#include "syscall.h"

#define A_DOWN_CASE (0)
#define A_UP_CASE   (1)
#define A_IGNORE    (-1)
#define A_CHAR_D    ('d')

void print_char(char c) {
    write(0, &c, 1);
}

void print_str(char* s) {
    while(*s){
        print_char(*s);
        ++s;
    }
}

int itoa(int i, int base, int aux, char* buf) {
    char i2a_map[16] = "0123456789abcdef";
    char i2a_map_up[16] = "0123456789ABCDEF";
    int r = 0; //the most right pos's value
    int j = 0; //itorator
    unsigned u = 0;
    unsigned _u = 0;

    u = (unsigned)i;
    if((u & 0x80000000) && ('d' == aux)) {//neg-pos
        u = (~u + 1);//get the 2's complement
        ++j;
        buf[0] = '-';
    }

    //calc out_buf_len
    _u = u;
    do {
        ++j;
        _u /= base;
    } while(_u);

    _u = u;
    r = _u % base;
    buf[j] = '\0';
    do {
        if(A_UP_CASE == aux) {
            buf[j - 1] = i2a_map_up[r];
        } else {
            buf[j - 1] = i2a_map[r];
        }
        --j;
        _u /= base;
        r = _u % base;
    } while(_u);
    return 0;
}

int strlen(const char * str) {
    const char *p = str;
    while(*p++);
    p -= 1;
    return (int)(p - str);
}

int vsprintf(char* buf, const char* format, va_list va) {
    const char* itf = format;
    int st = 0;
    char itoa_buf[32] = {0}, c;
    while(*itf) {
        switch(st) {
        case 0://idle
            if('%' == *itf) {
                st = 1;
            } else {
                *buf++ = *itf;
            }
            break;
        case 1:
            if ('x' == *itf) {//catch %x
                int i = va_arg(va, long);
                itoa(i, 16, A_DOWN_CASE, itoa_buf);
                int l = strlen(itoa_buf);
                memcpy(buf, itoa_buf, l);
                buf += l;
                st = 0;
            } else if('X' == *itf) {//catch %X
                int i = va_arg(va, long);
                itoa(i, 16, A_UP_CASE, itoa_buf);
                int l = strlen(itoa_buf);
                memcpy(buf, itoa_buf, l);
                buf += l;
                st = 0;
            } else if('c' == *itf) {//catch %c
                c = va_arg(va, int); //char is promoted to int when passed through ‘...’
                *buf++ = c;
                st = 0;
            } else if('s' == *itf) {//catch %s
                char* s = va_arg(va, char*);
                if(!s) {
                    s = "(null)";
                }

                int l = strlen(s);
                memcpy(buf, s, l);
                buf += l;
                st = 0;
            } else if('d' == *itf) {//catch %d
                int i = va_arg(va, int);
                itoa(i, 10, A_CHAR_D, itoa_buf);
                int l = strlen(itoa_buf);
                memcpy(buf, itoa_buf, l);
                buf += l;
                st = 0;
            } else if('u' == *itf) {//catch %u
                int i = va_arg(va, unsigned long);
                itoa(i, 10, A_IGNORE, itoa_buf);
                int l = strlen(itoa_buf);
                memcpy(buf, itoa_buf, l);
                buf += l;
                st = 0;
            } else if('%' == *itf) {
                *buf++ = '%';
                st = 0;
            } else {
                *buf++ = '%';
                *buf++ = *itf;
                char* s = "(not surport)";
                int l = strlen(s);
                memcpy(buf, s, l);
                buf += l;
                st = 0;
            }
            break;
        default:
            break;
        }
        ++itf;
    }
    return 0;
}


int printf(const char* format, ...) {
    char buf[512] = {0};
    va_list va;
    va_start(va, format);
    vsprintf(buf, format, va);
    print_str(buf);
    return 1;
}

int sprintf(char* buf, const char* format, ...) {
    va_list va;
    va_start(va, format);
    return vsprintf(buf, format, va);
}


int tc_printf(char tmp_text_color, const char* format, ...) {
    char buf[512] = {0};
    va_list va;
    char old_text_color = get_text_attr();
    set_text_attr(tmp_text_color);


    va_start(va, format);
    vsprintf(buf, format, va);
    print_str(buf);

    set_text_attr(old_text_color);
    return 0;
}

void *memcpy(void *dest, const void *src, unsigned int n) {
    char* d = dest;
    const char* s = src;
    while(n) {
        *d = *s;
        ++d;
        ++s;
        --n;
    }
    return dest;
}

void memset(void* dst, char c, int len) {
    char* d = (char*)dst;
    while(len) {
        *d = c;
        ++d;
        --len;
    }
}

char* strcpy(char* dest, const char* src) {
    int i = 0;
    for (; src[i] != '\0' ; i++) {
        dest[i] = src[i];
    }
    dest[i] = '\0';
    return dest;
}

int strcmp(const char* dst, const char* src) {
    int ret = 0;

    while (!(ret = *(unsigned char *) src - *(unsigned char *) dst) && *dst)
        ++src, ++dst;
    if (ret < 0) ret = -1;
    else if (ret > 0) ret = 1;
    return (ret);
}

int memcmp(const void *buffer1, const void *buffer2, int count) {
    if (!count) return (0);
    while (--count && *(char *) buffer1 == *(char *) buffer2) {
        buffer1 = (char *) buffer1 + 1;
        buffer2 = (char *) buffer2 + 1;
    }
    return (*((unsigned char *) buffer1) - *((unsigned char *) buffer2));
}


void clear_screen() {
    for(g_cur_pos = 0; g_cur_pos < 80 * 25 * 2; g_cur_pos += 2) {
        print_c(' ');
    }
    g_cur_pos = 0;
    di();
    out_byte(CRTC_ADDR_REG, CURSOR_H);
    out_byte(CRTC_DATA_REG, 0);
    out_byte(CRTC_ADDR_REG, CURSOR_L);
    out_byte(CRTC_DATA_REG, 0);
    si();
}

void set_text_attr(char attr) {
    g_text_attr = attr;
}

char get_text_attr() {
    return g_text_attr;
}

int isprint(int c) {
    return (c <= 0x7E && c >= 0x20);
}
